package com.tacitknowledge.filters.clustercheck;

import com.tacitknowledge.filters.GenericFilter;
import com.tacitknowledge.filters.clustercheck.ClusterCheckingHttpServletRequest;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ClusterCheckFilter extends GenericFilter {
	public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		if (this.getBooleanConfigEntry("ClusterCheckFilter.Enabled")) {
			ClusterCheckingHttpServletRequest requestWrapper = new ClusterCheckingHttpServletRequest(request);
			chain.doFilter(requestWrapper, response);
			boolean errors = false;
			if (this.getBooleanConfigEntry("ClusterCheckFilter.UnsetModificationCheck")
					&& this.checkUnsetModification(this.getFilterConfig().getServletContext(), requestWrapper)) {
				errors = true;
			}

			if (this.getBooleanConfigEntry("ClusterCheckFilter.ByteSizeCheck")) {
				if (this.checkAggregateSize(this.getFilterConfig().getServletContext(), requestWrapper)) {
					errors = true;
				}

				if (this.checkAttributeSizes(this.getFilterConfig().getServletContext(), requestWrapper)) {
					errors = true;
				}
			}

			if (errors && this.getBooleanConfigEntry("ClusterCheckFilter.ClientError")) {
				response.sendError(500, "Invalid session usage; see log for details");
			}
		} else {
			chain.doFilter(request, response);
		}

	}

	public void printBanner(ServletContext context) {
		context.log("$Id: ClusterCheckFilter.java,v 1.17 2005/03/12 01:52:28 mike Exp $");
		context.log("\tClusterCheckFilter.Enabled: " + this.getBooleanConfigEntry("ClusterCheckFilter.Enabled"));
		context.log("\tClusterCheckFilter.UnsetModificationCheck: "
				+ this.getBooleanConfigEntry("ClusterCheckFilter.UnsetModificationCheck"));
		context.log("\tClusterCheckFilter.ByteSizeCheck: "
				+ this.getBooleanConfigEntry("ClusterCheckFilter.ByteSizeCheck"));
		context.log("\t\tClusterCheckFilter.AttributeByteSizeLimit: "
				+ this.getIntConfigEntry("ClusterCheckFilter.AttributeByteSizeLimit", -1));
		context.log("\t\tClusterCheckFilter.AggregateByteSizeLimit: "
				+ this.getIntConfigEntry("ClusterCheckFilter.AggregateByteSizeLimit", -1));
		context.log(
				"\tClusterCheckFilter.ClientError: " + this.getBooleanConfigEntry("ClusterCheckFilter.ClientError"));
	}

	protected boolean checkUnsetModification(ServletContext context, ClusterCheckingHttpServletRequest wrapper) {
		List<String> errors = wrapper.validateSession();
		if (errors.size() <= 0) {
			return true;
		} else {
			context.log("The HttpSession is not being used in a cluster-friendly way -");
			Iterator<String> i = errors.iterator();

			while (i.hasNext()) {
				String error = (String) i.next();
				context.log(" --> " + error);
			}

			return false;
		}
	}

	protected boolean checkAggregateSize(ServletContext context, ClusterCheckingHttpServletRequest wrapper)
			throws IOException {
		int sizeLimit = this.getIntConfigEntry("ClusterCheckFilter.AggregateByteSizeLimit", -1);
		if (sizeLimit != -1) {
			int byteSize = wrapper.getAggregateSize();
			if (byteSize > sizeLimit) {
				context.log("AggregateByteSizeLimit exceeded, size: " + byteSize);
				return false;
			}
		}

		return true;
	}

	protected boolean checkAttributeSizes(ServletContext context, ClusterCheckingHttpServletRequest wrapper)
			throws IOException {
		boolean errors = false;
		int sizeLimit = this.getIntConfigEntry("ClusterCheckFilter.AttributeByteSizeLimit", -1);
		if (sizeLimit != -1) {
			Map<String, Integer> sizes = wrapper.getAttributeSizes();
			Iterator<String> sizeIter = sizes.keySet().iterator();

			while (sizeIter.hasNext()) {
				String attribute = (String) sizeIter.next();
				int byteSize = ((Integer) sizes.get(attribute)).intValue();
				if (byteSize > sizeLimit) {
					context.log("AttributeByteSizeLimit exceeded by " + attribute + ", of size: " + byteSize);
					errors = true;
				}
			}
		}

		return !errors;
	}
}